package lockMessage

import (
	"fmt"
	"gin-ranking/common/byteUtils"
	"gin-ranking/common/util"
	"strings"
)

// GlobalPackageHeaderFlag 十六进制，包头
var GlobalPackageHeaderFlag uint32 = 0xEF01EE02

type TcpMessage struct {

	/**
	 * 字段：消息的开头的信息标志
	 * 字段长度： 4
	 * 字段说明： 固定取值0XEF01EE02
	 */
	PackageHeaderFlag uint32

	/**
	 * 字段：包类型,
	 * 字段长度： 1
	 * 字段说明： 0X01表示请求包，0X11表示应答包
	 */
	PackageType uint32

	/**
	 * 字段：包类序号
	 * 字段长度： 2
	 * 字段说明： 从1开始，每次递增加1，应答包序号与请求包序号对应
	 */
	PackageSequence uint32

	/**
	 * 字段：包类标识
	 * 字段长度： 1
	 * 字段说明： "高4位表示包版本，低4位用来指示后面数据的加密类型，
	 * 加密类型取值说明，0：明文，1：AES128，2：SM4（事先约定密钥），3：SM4（设备指定密钥）"
	 */
	PackageVersion uint32

	/**
	 * 字段：包类标识
	 * 字段长度： 1
	 * 字段说明： "高4位表示包版本，低4位用来指示后面数据的加密类型，
	 * 加密类型取值说明，0：明文，1：AES128，2：SM4（事先约定密钥），3：SM4（设备指定密钥）"
	 */
	EncryptType uint32

	/**
	 * 字段：加密 密钥
	 */
	EncryptKey string

	/**
	 * 字段：签名 密钥
	 */
	SignKey string

	/**
	 * 字段：锁主键ID
	 * 字段长度： 20
	 * 字段说明： 锁主键ID
	 */
	LockId string

	/**
	 * 字段：命令码
	 * 字段长度： 2
	 * 字段说明： 执行命令代码
	 */
	ActionCode uint32

	/**
	 * 字段：命令状态码
	 * 字段长度： 2
	 * 字段说明： "命令执行结果，0为成功，1包格式错误，2解密出错，3未知命令码，4数据签名错误，5CRC错误，6数据长度异常，0xffff未知错误
	 * 对于请求包，默认填0"
	 */
	ActionStatus uint32

	/**
	 * 字段：数据长度
	 * 字段长度： 4
	 * 字段说明： "高16位用来指示后面数据块内容的长度，
	 * 低16位用来指示数据加密前的原长度"
	 */
	DataEncryptBeforeLength uint32

	/**
	 * 字段：数据长度
	 * 字段长度： 4
	 * 字段说明： "高16位用来指示后面数据块内容的长度，
	 * 低16位用来指示数据加密前的原长度"
	 */
	DataEncryptAfterLength uint32

	/**
	 * 字段：数据块(组装成的数据块)
	 * 字段长度： 可变长度
	 * 字段说明： 业务数据内容，长度变长
	 */
	DataByes []byte

	/**
	 * 字段：数据块(解密后的原始业务数据块)
	 * 字段长度： 可变长度
	 * 字段说明： 业务数据内容，长度变长
	 */
	OriginBizDataBytes []byte

	/**
	 * 字段：数据块(CRC)
	 */
	CrcByes []byte
}

// NewTcpMessage 对象
func NewTcpMessage(packageType int, encryptType int, lockId string, actionCode int, actionStatus int,
	dataEncryptBeforeLength int, dataEncryptAfterLength int, dataBytes []byte) TcpMessage {
	tcpMessage := TcpMessage{
		PackageHeaderFlag:       GlobalPackageHeaderFlag,
		PackageType:             uint32(packageType),
		PackageSequence:         1,
		PackageVersion:          1,
		EncryptType:             uint32(encryptType),
		LockId:                  lockId,
		ActionCode:              uint32(actionCode),
		ActionStatus:            uint32(actionStatus),
		DataEncryptBeforeLength: uint32(dataEncryptBeforeLength),
		DataEncryptAfterLength:  uint32(dataEncryptAfterLength),
		DataByes:                dataBytes,
	}
	return tcpMessage
}

// Decoder 解码  EF01EE02010000005450365F6633636333343037363864340000000010100000001A001A674EC14F0000DF180B1B9BF6BD21AA5E12A8AF37187788442CCD1524
func Decoder(dataBytes []byte) TcpMessage {

	PackageHeaderFlag := util.BytesToUint32Big(dataBytes[0:4])
	fmt.Println("消息的开头的信息标志：", PackageHeaderFlag)

	if PackageHeaderFlag != GlobalPackageHeaderFlag {
		fmt.Println("包头格式不正确")
	}

	PackageType := util.BytesToUint32Big(util.StandardBefore(dataBytes[4:5], 4))
	fmt.Println("包类型，0X01表示请求包，0X11表示应答包：", PackageType)

	PackageSequence := util.BytesToUint32Big(util.StandardBefore(dataBytes[5:7], 4))
	fmt.Println("包序号：", PackageSequence)

	PackageVersion := util.BytesToUint32Big(util.StandardBefore(dataBytes[7:8], 4)) >> 4
	fmt.Println("包版本：", PackageVersion)

	EncryptType := util.BytesToUint32Big(util.StandardBefore(dataBytes[7:8], 4)) & 0b1111
	fmt.Println("加密类型：", EncryptType)

	LockId := util.BytesToString(dataBytes[8:28])
	fmt.Println("锁ID：", LockId)

	ActionCode := util.BytesToUint32Big(util.StandardBefore(dataBytes[28:30], 4))
	fmt.Println("命令码：", ActionCode)

	ActionStatus := util.BytesToUint32Big(util.StandardBefore(dataBytes[30:32], 4))
	fmt.Println("命令状态：", ActionStatus)

	EncryptAfterLength := (util.BytesToUint32Big(util.StandardBefore(dataBytes[32:36], 4)) >> 16) & 0xffff
	fmt.Println("数据加密后的长度：", EncryptAfterLength)

	EncryptBeforeLength := (util.BytesToUint32Big(util.StandardBefore(dataBytes[32:36], 4)) & 0b1111111111111111) & 0xffff
	fmt.Println("数据加密前的长度：", EncryptBeforeLength)

	DataBytes := dataBytes[36 : len(dataBytes)-2]
	fmt.Println("数据：", DataBytes)

	CrcByes := dataBytes[len(dataBytes)-2 : len(dataBytes)]
	fmt.Println("CRC数据校验：", CrcByes)

	//校验位
	CrcCodeBytes := util.Crc16(dataBytes[0 : len(dataBytes)-2])

	if !strings.EqualFold(util.BytesToHex(CrcByes), util.BytesToHex(CrcCodeBytes)) {
		fmt.Printf("crc16校验并不正确,您传入的是: %X, 计算的是: %X\n", CrcByes, CrcCodeBytes)
	}

	tcpMessage := TcpMessage{
		PackageHeaderFlag:       PackageHeaderFlag,
		PackageType:             PackageType,
		PackageSequence:         PackageSequence,
		PackageVersion:          PackageVersion,
		EncryptType:             EncryptType,
		LockId:                  LockId,
		ActionCode:              ActionCode,
		ActionStatus:            ActionStatus,
		DataEncryptBeforeLength: EncryptBeforeLength,
		DataEncryptAfterLength:  EncryptAfterLength,
		DataByes:                DataBytes,
		CrcByes:                 CrcByes,
	}
	return tcpMessage
}

// Encoder 编码请求字节数组
func Encoder(msg TcpMessage) []byte {

	// 包头数组（固定取值0XEF01EE02）
	PackageHeaderBytes := util.Uint32ToBytesBig(GlobalPackageHeaderFlag)

	// 包类型（0X01表示请求包，0X11表示应答包）
	PackageTypeBytes := util.Uint32ToBytesBig(msg.PackageType)[3:4]

	// 包序号（ 从1开始，每次递增加1，应答包序号与请求包序号对应）
	PackageSequenceBytes := util.Uint32ToBytesBig(msg.PackageSequence)[2:4]

	// 包标识（高4位表示包版本，低4位用来指示后面数据的加密类型，0：明文，1：AES128，2：SM4（事先约定密钥），3：SM4）
	PackageMarkBytes := util.Uint32ToBytesBig((msg.PackageVersion << 4) + msg.EncryptType)[3:4]

	// 锁id
	PackageLockIdBytes := util.StandardAfter(util.StringToBytes(msg.LockId), 20)

	//命令码
	ActonCodeBytes := util.Uint32ToBytesBig(msg.ActionCode)[2:4]

	// 命令执行状态 ， 请求默认为：0
	ActionStatusBytes := util.Uint32ToBytesBig(msg.ActionStatus)[2:4]

	// 数据长度
	DataLengthBytes := util.Uint32ToBytesBig((msg.DataEncryptAfterLength << 16) + msg.DataEncryptBeforeLength)

	// 数据块 -- 是否加密
	DataBytes := msg.DataByes

	// CRC 之前数据（从包头到数据块）
	BeforeCRCBytes := util.BytesAppend(PackageHeaderBytes, PackageTypeBytes,
		PackageSequenceBytes, PackageMarkBytes, PackageLockIdBytes,
		ActonCodeBytes, ActionStatusBytes, DataLengthBytes, DataBytes)

	//校验位
	CrcCodeBytes := util.Crc16(BeforeCRCBytes)

	// 返回拼接校验位
	return util.BytesAppend(BeforeCRCBytes, CrcCodeBytes)

}

// IndexOf 判断索两个buf包含关系开始引值
func IndexOf(in *byteUtils.ByteBuf, needle *byteUtils.ByteBuf) int {

	for i := in.ReaderIndex(); i < in.WriterIndex(); i++ {
		haystackIndex := i
		var needleIndex int
		for needleIndex = 0; needleIndex < needle.Capacity(); needleIndex++ {
			getByteIndex, _ := in.GetByte(haystackIndex)
			getByteNeedleIndex, _ := needle.GetByte(needleIndex)

			if getByteIndex != getByteNeedleIndex {
				break
			}
			haystackIndex++
			if haystackIndex == in.WriterIndex() && needleIndex != needle.Capacity()-1 {
				return -1
			}
		}
		if needleIndex == needle.Capacity() {
			return i - in.ReaderIndex()
		}
	}
	return -1
}

// GetBeginDelimiter 读取包头
func GetBeginDelimiter() *byteUtils.ByteBuf {
	beginBuf, _ := byteUtils.NewByteBufWithCapacity(0, []byte{})
	beginBuf.WriteInt32BE(int32(GlobalPackageHeaderFlag))
	return beginBuf
}

// DecoderMsg 解码
func DecoderMsg(cacheBuf *byteUtils.ByteBuf) TcpMessage {

	// 获取包头的索引值
	beginBeforeIndex := IndexOf(cacheBuf, GetBeginDelimiter())

	// 读取废弃数据,并且废弃
	if beginBeforeIndex > 0 {
		bytes := make([]byte, beginBeforeIndex)
		cacheBuf.ReadBytes(bytes)
	}

	// 获取可读取数据长度
	readableLength := cacheBuf.ReadableBytes()

	// 如果可读数据，小于基本数据，返回，不处理
	if readableLength < 36 {
		return TcpMessage{}
	}

	// 获取可读索引位置
	beginReader := cacheBuf.ReaderIndex()

	// 读取业务之前的数据
	receiveDataBefore36Bytes := make([]byte, 36)
	cacheBuf.ReadBytes(receiveDataBefore36Bytes)

	// 获取业务数据的长度
	dataLength := (util.BytesToUint32Big(receiveDataBefore36Bytes[36-4:36]) >> 16) & 0xffff

	// 如果业务数据小于16字节，或者大于100K ，认为是脏数据不处理, 废弃读完的数据
	if dataLength < 16 || dataLength > 100*1024 {
		cacheBuf.DiscardReadBytes()
		return TcpMessage{}
	}

	// 获取可读取业务数据长度
	readableBizLength := cacheBuf.ReadableBytes()

	// 可读取业务数据长度,不够，则返回
	if readableBizLength < int(dataLength+2) { // 还原读指针
		cacheBuf.SetReaderIndex(uint(beginReader))
		return TcpMessage{}
	}

	// 读取业务数据
	bizDataBytes := make([]byte, dataLength)
	cacheBuf.ReadBytes(bizDataBytes)

	// crc校验数据数据
	CRCBytes := make([]byte, 2)
	cacheBuf.ReadBytes(CRCBytes)

	// 拼接业务数据
	dataBytes := util.BytesAppend(receiveDataBefore36Bytes, bizDataBytes, CRCBytes)

	// 解码
	msg := Decoder(dataBytes)

	// 废弃读完的数据
	cacheBuf.DiscardReadBytes()

	return msg
}
